package 方法句柄;

//import static org.junit.Assert.assertEquals;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.invoke.MethodType.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/*public class Test {

	public static void main(String[] args) throws Throwable {
		MethodHandles.Lookup ml = MethodHandles.lookup();
		MethodType mt = MethodType.methodType(String.class, int.class, int.class);
		MethodHandle mh = ml.findVirtual(String.class, "substring", mt);
		// Object str=mh.invokeExact("Hello",2,3);//出错
		String str = (String) mh.invokeExact("Hello", 2, 4);// Ok
		System.out.println(str);
		Object str1 = mh.invoke("Hello", 2, 4);// Ok
		System.out.println(str1);

		MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();
		MethodType mt1 = MethodType.methodType(String.class, char.class, char.class);
		MethodHandle replaceMH = publicLookup.findVirtual(String.class, "replace", mt1);
		String replacedString = (String) replaceMH.invoke("jovo", Character.valueOf('o'), 'a');
		String replacedString3 = (String) replaceMH.invoke("jovo", 'o', 'a');
		String replacedString2 = (String) replaceMH.invoke("jovo", Character.valueOf('o'), 'a');
		String replacedString4 = (String) replaceMH.invokeExact("jovo", 'o', 'a');
		//String replacedString5 = (String) replaceMH.invokeExact("jovo", Character.valueOf('o'), 'a'); // 不能使用包装类，报错
		
		//通过invokeWithArguments方法实现
		//最后一种调用方式是使用invokeWithArguments。该方法在调用时可以指定任意多个Object类型的参数。完整的调用方式是首先根据传入的实际参数的个数.

		//1.通过MethodType的genericMethodType方法得到一个返回值和参数类型都是Object的新方法类型。
		//2.再把原始的方法句柄通过asType转换后得到一个新的方法句柄。
		//3.最后通过新方法句柄的invokeExact方法来完成调用。
		//这个方法相对于invokeExact和invoke的优势在于，它可以通过Java反射API被正常获取和调用，而invokeExact和invoke不可以这样。它可以作为反射API和方法句柄
		//之间的桥梁。

		MethodType mt2 = MethodType.methodType(List.class, Object[].class);

		MethodHandle asList1 = publicLookup.findStatic(Arrays.class, "asList", mt2);

		List<Integer> list = (List<Integer>) asList1.invokeWithArguments(1,2,3);
		System.out.println(list.toString());
		System.out.println(Arrays.asList(1,2));
		//assertThat(Arrays.asList(1,2), is(list));
		
		MethodHandles.Lookup lookup=MethodHandles.lookup();
		MethodHandle mh3=lookup.findVirtual(String.class,"length",MethodType.methodType(int.class));
		int len=(int)mh3.invoke("Hello");
		System.out.println(len);//值为5
		mh3=mh3.bindTo("Hello World");
		len=(int)mh3.invoke();
		System.out.println(len);//值为11
		
	    //MethodHandles.Lookup lookup = MethodHandles.lookup();
		//public int indexOf(String str, int fromIndex)
		MethodType mt4 = MethodType.methodType(int.class, String.class, int.class);
	    MethodHandle mh4 = lookup.findVirtual(String.class,"indexOf",mt4);
	    mh4 = mh4.bindTo("Hello").bindTo("e");
	    int index = "Hello".indexOf("o",0);
	    System.out.println(index);
	    System.out.println(mh4.invoke(0));
	    //assertEquals(index, mh.invoke(2)); // true
	    
	    //无法为int和float这样的基本类型绑定值。对于包含基本类型参数的方法句柄，可以先使用wrap方法把方法类型中的基本类型转换成对应的包装类，
	    //再通过方法句柄的asType将其转换成新的句柄。转换之后的新句柄就可以通过bindTo来进行绑定，
	    //public String substring(int beginIndex, int endIndex)
	    MethodType mt5=MethodType.methodType(String.class, int.class,int.class);
	    MethodHandle mh5=lookup.findVirtual(String.class,"substring",mt5);
	    mh5=mh5.asType(mh5.type().wrap());
	    MethodHandle mh51=mh5.bindTo("Hello World").bindTo(3);//绑定作用实例，再绑定第一个参数
	    MethodHandle mh52=mh5.bindTo("Hello World").bindTo(3).bindTo(5);//绑定作用实例，再绑定第一个参数及第二个参数
	    String  str5 = "Hello World".substring(3,5);
	    System.out.println(mh51.invoke(5));//值为“lo”
	    System.out.println(mh52.invoke());//值为“lo”
	    //assertEquals(str, mh.invoke(5));
	}
}
*/

/*public class Test {
	public static void AnimalRace(Object obj) throws Throwable {
		MethodHandles.Lookup lookup = MethodHandles.lookup();
		MethodType mt=MethodType.methodType(void.class);
		MethodHandle mh1=lookup.findStatic(obj.getClass(),"race",mt);
		MethodHandle mh2=lookup.findVirtual(obj.getClass(),"say",mt);
		mh1.invoke();
		mh2.invoke(obj);
		}
	public static void main(String[] args) throws Throwable {
		AnimalRace(new CobraT());
	}
}

class CobraT {
	public static void race() {
		System.out.println("Cobra run");
	}

	public void say() {
		System.out.println("Cobra say");
	}
}*/

//操作类的私有字段，必须在搜索的类中建立Lookup对象。
/*class CobraT {

	private String name;

	public static Lookup lookup() {

		return MethodHandles.lookup();
	}
}

public class Test {

	public static void main(String[] args) throws Throwable {

		MethodHandles.Lookup lookup = CobraT.lookup();
		MethodHandle methodHandle = lookup.findSetter(CobraT.class, "name", String.class);
		CobraT cobra = new CobraT();
		methodHandle.invokeExact(cobra, "111");
		MethodHandle getterMethodHandle = lookup.findGetter(CobraT.class, "name", String.class);
		System.out.println(getterMethodHandle.invoke(cobra));
	}
}*/

//1、方法句柄的访问权限不取决于方法句柄的创建位置，而是取决于 Lookup 对象的创建位置。
//2、如果 JDK 版本大于8，那么可以在其他类中，也能够通过该 Lookup 对象对类私有属性进行赋值、取值操作。
//需要注意的是，JDK9 中权限更大了，也存在一些隐患。由于方法句柄没有运行时权限检查，因此，应用程序需要负责方法句柄的管理。一旦它发布了某些指向私有方法的方
//法句柄，那么这些私有方法便被暴露出去了。如下述案例所示：
/*class CobraT {
	static {
		System.out.println("啊是的");
	}
	private void eat() {
		System.out.println("eat");
	}
}

public class Test {

	public static void main(String[] args) throws Throwable {

		MethodHandles.Lookup lookup = MethodHandles.lookup();
		System.out.println(lookup.toString());
		Lookup privateLookupIn = MethodHandles.privateLookupIn(CobraT.class, lookup);
		MethodType methodType = MethodType.methodType(void.class);
		MethodHandle methodHandle = privateLookupIn.findSpecial(CobraT.class, "eat", methodType, CobraT.class);
		methodHandle.invoke(new CobraT());
	}
}*/

//public MethodHandle findSpecial​(类<?> refc, String name, MethodType type, 类<?> specialCaller) throws NoSuchMethodException, IllegalAccessException
//refc - 从中访问方法的类或接口
//name - 方法的名称（不能是“<init>”）
//type - 方法的类型，省略receiver参数
//specialCaller - 建议的调用类执行 invokespecial
/*public class Test {
	static class Listie extends ArrayList {
		public String toString() {
			return "[wee Listie]"; 
		}
		static Lookup lookup() {
			return MethodHandles.lookup(); 
		}
	}
	public static void main(String[] args) throws Throwable {
		MethodType mt1=MethodType.methodType(void.class);
		MethodHandle MH_newListie = Listie.lookup().findConstructor(Listie.class, mt1);//获得构造器方法句柄
		Listie l = (Listie) MH_newListie.invokeExact();//通过获得的构造器实例化Listie 相当于：Listie l=new Listie()
		Listie ll=new Listie();
		System.out.println(l);//[wee Listie]
		System.out.println(ll);//[wee Listie]
		
		//不能调用类内部方法<init>实例化类：
		try {
			assertEquals("impossible", Listie.lookup().findSpecial(Listie.class, "<init>", MethodType.methodType(void.class), Listie.class));
		} catch (NoSuchMethodException ex) {
			 System.out.println("impossible");
		} // OK
		
		MethodType mt2=MethodType.methodType(String.class);
		MethodHandle MH_super = Listie.lookup().findSpecial(ArrayList.class, "toString" , mt2, Listie.class);
		MethodHandle MH_this = Listie.lookup().findSpecial(Listie.class, "toString" , mt2, Listie.class);
		MethodHandle MH_duper = Listie.lookup().findSpecial(Object.class, "toString" , mt2, Listie.class);
		assertEquals("[]", (String) MH_super.invokeExact(l));//调用父类toString方法
		assertEquals(""+l, (String) MH_this.invokeExact(l));//调用this.toString方法
		assertEquals("[]", (String) MH_duper.invokeExact(l)); //调用Object.toString方法 ArrayList method
		
		try { 
			assertEquals("inaccessible", Listie.lookup().findSpecial(String.class, "toString", mt2, Listie.class));
		 } catch (IllegalAccessException ex) { } // OK
		Listie subl = new Listie() {
			public String toString() {
				return "[subclass]";
				}
			};
		assertEquals(""+l, (String) MH_this.invokeExact(subl)); // Listie method
	}
}*/
/*
class GrandFather {

	protected void thinking() {
        System.out.println("I 'm grandFather!");
    }
    private void hello() {
    	System.out.println("grandFather say hello !!!");
    }
}

class Father extends GrandFather {

    @Override
    protected void thinking() {
        System.out.println("I 'm father!");
    }

    private void hello() {
        System.out.println("father say hello !!!");
    }
}

class Son extends Father {

    @Override
    protected void thinking() {
        System.out.println("I 'm Son!");
    }
}

class mhUtil{
	//通过反射获取MethodHandles类中privateLookupIn方法句柄
	private static Method privateLookupInMethod;
	static {
		//public static Lookup privateLookupIn(Class<?> targetClass, Lookup caller)
		try {
			privateLookupInMethod=MethodHandles.class.getMethod("privateLookupIn", Class.class,MethodHandles.Lookup.class);
			//                                                            Class<?> targetClass,Lookup caller
			System.out.println(privateLookupInMethod.toString());
		} catch (NoSuchMethodException e){
			e.printStackTrace();
		}
	}
	//再通过privateLookupIn方法句柄，构造MethodHandles.Lookup，参数数为Callerclass,即：私有方法拥有者
	public static MethodHandles.Lookup lookup(Class<?> Callerclass){
		try {
			MethodHandles.Lookup mhs=(MethodHandles.Lookup)privateLookupInMethod.invoke(MethodHandles.class,Callerclass, MethodHandles.lookup());
			//System.out.println(mhs.toString());
			return mhs;
		}catch(IllegalAccessException | InvocationTargetException e) {
			throw new RuntimeException(e);
		}
	}

	//直接使用privateLookupIn方法 构造MethodHandles.Lookup，参数数为Callerclass,即：私有方法拥有者
//	public static MethodHandles.Lookup lookup(Class<?> Callerclass){
//		try {
//			return MethodHandles.privateLookupIn(Callerclass, MethodHandles.lookup());
//			
//		}catch (IllegalAccessException e) {
//			throw new RuntimeException(e);
//		}
//	}
}

//public MethodHandle findSpecial(Class<?> refc, String name, MethodType type,Class<?> specialCaller)
public class Test {
	public static void main(String[] args) throws Throwable {
		Son son=new Son();
		son.thinking();//I 'm Son!
		Father father=new Father();
		MethodType mt1=MethodType.methodType(void.class);
		
		//Son <: Father <: GrandFather，而Father与GrandFather类上都有自己的thinking()方法的实现，因而从Son出发查找就会找到其直接父
		//类Father上的thinking()，即便传给findSpecial()的第一个参数是GrandFather。
		MethodHandles.Lookup mhl0=mhUtil.lookup(Son.class);
		MethodHandle mh0=mhl0.findSpecial(Father.class, "thinking", mt1, Son.class);
		mh0.invoke(son);
		
		MethodHandles.Lookup mhl1=mhUtil.lookup(Father.class);//
		System.out.println("---"+mhl1.toString());//方法句柄.Father
		
		System.out.println(mt1.toString());//()void
		MethodHandle mh1=mhl1.findSpecial(Father.class, "thinking", mt1, Father.class);
		mh1.invoke(son);//I 'm father!
		MethodHandle mh11=mhl1.findSpecial(GrandFather.class, "thinking", mt1, Father.class);
		mh11.invoke(son);//I 'm grandFather!
		
		MethodHandle mh12=mhl1.findSpecial(Father.class, "hello", mt1, Father.class);
		mh12.invoke(son);//father say hello !!!
		
		MethodHandles.Lookup mhl2=mhUtil.lookup(GrandFather.class);
		System.out.println(mhl2.toString());//方法句柄.GrandFather
		MethodType mt2=MethodType.methodType(void.class);
		MethodHandle mh2=mhl2.findSpecial(GrandFather.class, "thinking", mt2, GrandFather.class);
		System.out.println("father");
		mh2.invoke(father);//I 'm grandFather!
		
		//反射调用
        Method hello1 = Father.class.getDeclaredMethod("hello");
        hello1.setAccessible(true);
        hello1.invoke(son);//father say hello !!!
        
        //调用父类中的私有方法,因为子类没法重写父类的私有方法,所以调用结果跟反射一样.
        MethodHandles.Lookup lookup1 = mhUtil.lookup(Father.class);
        MethodHandle methodHandle1 = lookup1.unreflectSpecial(hello1, Father.class);
        methodHandle1.invoke(son);//father say hello !!!
        
        //调用祖父类中的私有方法
        MethodHandles.Lookup lookup2 = mhUtil.lookup(GrandFather.class);
        Method hello2=GrandFather.class.getDeclaredMethod("hello");
//        hello2.setAccessible(false);
        MethodHandle methodHandle2 = lookup2.unreflectSpecial(hello2, GrandFather.class);
        methodHandle2.invoke(son);//grandFather say hello !!!
	}
}*/
/*public class Test {
	class GrandFather {
		void thinking() {
			System.out.println("i am grandfather");
		}
	}

	class Father extends GrandFather {
		void thinking() {
			System.out.println("i am father");
		}
	}

	class Son extends Father {
		void thinking() {
			try {
				
				//IMPL_LOOKUP是依赖判断私有方法是否被信任的标识，用来控制访问权限，默认是false.
				//默认情况下，findSpecial方法中最后一个参数Class<?> specialCaller有一个检验checkSpecialCaller.
				//如果当前的lookup的类和specialCaller不一致的化就会检测不通过。
				//IMPL_LOOKUP.setAccessible(true);设置为true后，
				//(MethodHandles.Lookup) lookupImpl.get(null)这里获取一个Lookup,
				//这种方式返回的allowedModes为-1,这样的话就可以绕过检查，从而执行传入的specialCaller类中的方法，
				//当然也有风险，舍弃了强校验，很容易抛出NoSuchMethodError
				MethodType mt = MethodType.methodType(void.class);
				Field lookupImpl = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP");
				System.out.println(lookupImpl.toString());
				lookupImpl.setAccessible(true);
				MethodHandles.Lookup ml=(MethodHandles.Lookup) lookupImpl.get(null);
				System.out.println(ml.lookupModes());
				MethodHandle mh = ((MethodHandles.Lookup) lookupImpl.get(null)).findSpecial(GrandFather.class,
						"thinking", mt, GrandFather.class);
				mh.invoke(this);
			} catch (Throwable e) {
				e.printStackTrace();
			}
		}

	}

	public static void main(String[] args) {
		Son son=new Test().new Son();
		son.thinking();
	}
}*/

/*
class Cobra1 { 
    private void money(){ 
      System.out.println("money");  
    }
}

class Util{
	public static MethodHandles.Lookup lookup(Class<?> CallerClass) throws Throwable{
		return MethodHandles.privateLookupIn(CallerClass, MethodHandles.lookup());
	}
}
public class Test { 
    public static void main(String[] args) throws Throwable { 
    	MethodHandles.Lookup lp = Util.lookup(Cobra1.class);    
    	Cobra1 cobra = (Cobra1) new Cobra1();    
//    	Lookup privateLookupIn = MethodHandles.privateLookupIn(Cobra.class, lookup);    
    	MethodHandle methodHandle = lp.findSpecial(Cobra1.class, "money", MethodType.methodType(void.class),Cobra1.class);
    	methodHandle.invoke(cobra);
    	
    	MethodHandles.Lookup lp0=MethodHandles.privateLookupIn(Cobra1.class,MethodHandles.lookup());
    	MethodHandle methodHandle0 = lp0.findSpecial(Cobra1.class, "money", MethodType.methodType(void.class),Cobra1.class);
    	methodHandle0.invoke(cobra);
    }
}
*/


